package br.uff.es2.xadrez.core;

import br.uff.es2.xadrez.core.exceptions.CheckMateException;
import br.uff.es2.xadrez.core.exceptions.PromocaoDePecasException;
import br.uff.es2.xadrez.core.exceptions.RoqueException;
import br.uff.es2.xadrez.core.exceptions.WrongColorTurnException;
import br.uff.es2.xadrez.core.exceptions.WrongMoveException;
import br.uff.es2.xadrez.gui.PromocaoPecas;
import javax.swing.JOptionPane;

public class Board {

    public static Piece[][] board;
    private Piece.Color turn = Piece.Color.WHITE;
    public static UnPassant unpassant;
    public static Roque roque;
    public static PromocaoPecas promocao;
    private int check = 0;
    private int[][] kingPosition = new int[2][2];
    private boolean turnChange = false;

    public Board() {

        board = new Piece[8][8];
        board[0][0] = new Rook(Piece.Color.BLACK, "img/torre.png");
        board[0][1] = new Knight(Piece.Color.BLACK,"img/cavalo.png");
        board[0][2] = new Bishop(Piece.Color.BLACK, "img/bispo.png");
        board[0][3] = new Queen(Piece.Color.BLACK, "img/rainha.png");
        board[0][4] = new King(Piece.Color.BLACK, "img/rei.png");
        board[0][5] = new Bishop(Piece.Color.BLACK, "img/bispo.png");
        board[0][6] = new Knight(Piece.Color.BLACK, "img/cavalo.png");
        board[0][7] = new Rook(Piece.Color.BLACK, "img/torre.png");
        for (int i = 0; i < 8; i++) {
            board[1][i] = new Pawn(Piece.Color.BLACK, "img/peao.png");
        }

        board[7][0] = new Rook(Piece.Color.WHITE, "img/torre-branca.png");
        board[7][1] = new Knight(Piece.Color.WHITE, "img/cavalo-branco.png");
        board[7][2] = new Bishop(Piece.Color.WHITE, "img/bispo-branco.png");
        board[7][3] = new Queen(Piece.Color.WHITE, "img/rainha-branca.png");
        board[7][4] = new King(Piece.Color.WHITE, "img/rei-branco.png");
        board[7][5] = new Bishop(Piece.Color.WHITE, "img/bispo-branco.png");
        board[7][6] = new Knight(Piece.Color.WHITE, "img/cavalo-branco.png");
        board[7][7] = new Rook(Piece.Color.WHITE, "img/torre-branca.png");
        for (int i = 0; i < 8; i++) {
            board[6][i] = new Pawn(Piece.Color.WHITE, "img/peao-branco.png");
        }
        roque = new Roque();
        unpassant = new UnPassant();
        promocao = new PromocaoPecas();
    }

    @SuppressWarnings("empty-statement")
    public int[] move(int[] oldPosition, int[] newPosition) throws WrongMoveException, RoqueException, CheckMateException, PromocaoDePecasException {

        int[] posicaoEspecial = null;
        Piece piece = board[oldPosition[0]][oldPosition[1]];
        Piece enemyPiece = board[newPosition[0]][newPosition[1]];
        if (!piece.color.equals(turn)) {
            throw new WrongColorTurnException();
        }

        if (unpassant.getColor().equals(getTurn())) {
            unpassant.setEnPassantPossible(false);
        }

        int[][] avaiableMoves = piece.getAvaiableMoves(oldPosition, board);

        if (avaiableMoves[newPosition[0]][newPosition[1]] == 1) {
            if (check == 1) {
                Piece.Color nextTurn = getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE;
                setPiece(null, oldPosition);
                setPiece(piece, newPosition);
                check = verifyCheck(nextTurn, true);
                if (check == 0) {
                    piece.setMoved(true);
                    check = verifyCheck(getTurn(), false);
                    setTurn(getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE);
                    turnChange = true;
                } else {
                    turnChange = false;
                    setPiece(piece, oldPosition);
                    setPiece(enemyPiece, newPosition);
                    throw new WrongMoveException();
                }
            } else {
                setPiece(null, oldPosition);
                setPiece(piece, newPosition);
                check = verifyCheck(getTurn(), false);
                if (check == 2) {
                    turnChange = false;
                    setPiece(piece, oldPosition);
                    setPiece(enemyPiece, newPosition);
                    throw new WrongMoveException();
                }
                piece.setMoved(true);
                setTurn(getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE);
                turnChange = true;
            }
        } else if (avaiableMoves[newPosition[0]][newPosition[1]] == 2) {
            setPiece(null, unpassant.getPosicao());
            setPiece(null, oldPosition);
            setPiece(piece, newPosition);
            check = verifyCheck(getTurn(), false);
            setTurn(getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE);
            turnChange = true;
            posicaoEspecial = unpassant.getPosicao();
//            throw new UnPassantException();
        } else if (avaiableMoves[newPosition[0]][newPosition[1]] == 3) {
            Piece torre = board[0][0]; //instancia o componente piece atraves de um posicao qualquer
            if (roque.getCor().equals(Piece.Color.WHITE)) {
                if (newPosition[1] > 4) {
                    torre = board[7][7]; //busca o componente da torre do lado esquerdo das brancas
                    int posicaoAntigaTorre[] = {7, 7};
                    Board.roque.setPosicaoAntigaTorre(posicaoAntigaTorre);
                    int posicaoNovaTorre[] = {7, 5};
                    Board.roque.setPosicaoNovaTorre(posicaoNovaTorre);
                } else {
                    torre = board[7][0]; //busca o componente da torre do lado direito das brancas
                    int posicaoAntigaTorre[] = {7, 0};
                    Board.roque.setPosicaoAntigaTorre(posicaoAntigaTorre);
                    int posicaoNovaTorre[] = {7, 3};
                    Board.roque.setPosicaoNovaTorre(posicaoNovaTorre);
                }
            } else {
                if (newPosition[1] > 4) {
                    torre = board[0][7]; //busca o componente da torre do lado esquerdo das pretas(pequeno roque)
                    int posicaoAntigaTorre[] = {0, 7};
                    Board.roque.setPosicaoAntigaTorre(posicaoAntigaTorre);
                    int posicaoNovaTorre[] = {0, 5};
                    Board.roque.setPosicaoNovaTorre(posicaoNovaTorre);
                } else {
                    torre = board[0][0]; //busca o componente da torre do lado direito das pretas(grande roque)
                    int posicaoAntigaTorre[] = {0, 0};
                    Board.roque.setPosicaoAntigaTorre(posicaoAntigaTorre);
                    int posicaoNovaTorre[] = {0, 3};
                    Board.roque.setPosicaoNovaTorre(posicaoNovaTorre);
                }
            }

            setPiece(null, Board.roque.getPosicaoAntigaTorre());
            setPiece(torre, Board.roque.getPosicaoNovaTorre());

            setPiece(null, oldPosition);
            setPiece(piece, newPosition);
            piece.setMoved(true);
            setTurn(getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE);
            turnChange = true;
            throw new RoqueException();

        } else if (avaiableMoves[newPosition[0]][newPosition[1]] == 4) {
            //Promocao
            setPiece(null, oldPosition);
            PromocaoPecas.novaPosicao = newPosition;
            turn = getTurn().equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE;
            turnChange = true;
            throw new PromocaoDePecasException();
        } else {
            throw new WrongMoveException();
        }

        if (check == 1 && turnChange) {
            if (verifyCheckMate(getTurn())) {
                System.out.println("CHECKMATE!!");
                JOptionPane.showMessageDialog(null, "CHECKMATE!!", null, JOptionPane.INFORMATION_MESSAGE);
                throw new CheckMateException();
                //acabar o jogo aqui
                } else {
                JOptionPane.showMessageDialog(null, "CHECK ON " + turn + "!", "Check", JOptionPane.INFORMATION_MESSAGE);
            }
        }
        return posicaoEspecial;

    }

    void somaMatriz(int[][] checkBoard, int[][] matriz) {
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                checkBoard[i][j] += matriz[i][j];
            }
        }
    }

    //retorna 0 se a jogada nao deixou o proximo jogador em xeque, 1 se o proximo jogador ficou em xeque,
    //2 se o jogador atual ficaria em situacao de xeque ao fazer a jogada, 3 se xeque-mate
    int verifyCheck(Piece.Color turn, boolean check) {
        int[][] whiteCheckBoard = new int[8][8];
        int[][] blackCheckBoard = new int[8][8];
        int[][] matriz = new int[8][8];
        int[] position = new int[2];
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                if (board[i][j] != null) {
                    position[0] = i;
                    position[1] = j;
                    if (board[i][j] instanceof Pawn) {
                        matriz = ((Pawn) board[i][j]).getCaptureMoves(position, board);
                    } else {
                        matriz = board[i][j].getAvaiableMoves(position, board);
                    }
                    if (board[i][j].getColor().equals(Piece.Color.BLACK)) {
                        somaMatriz(whiteCheckBoard, matriz);
                        if (board[i][j] instanceof King) {
                            kingPosition[1][0] = i;
                            kingPosition[1][1] = j;
                        }
                    } else {
                        somaMatriz(blackCheckBoard, matriz);
                        if (board[i][j] instanceof King) {
                            kingPosition[0][0] = i;
                            kingPosition[0][1] = j;
                        }
                    }
                }
            }
        }
        if (turn.equals(Piece.Color.WHITE)) {
//            System.out.println(whiteCheckBoard[kingPosition[0][0]][kingPosition[0][1]]);
//            System.out.println(blackCheckBoard[kingPosition[1][0]][kingPosition[1][1]]);
            if (!check) {
                if (whiteCheckBoard[kingPosition[0][0]][kingPosition[0][1]] > 0) {
                    return 2;
                }
            }
            if (blackCheckBoard[kingPosition[1][0]][kingPosition[1][1]] > 0) {
//                System.out.println("preto ameacado");
//                if (verifyCheckMate(turn)) {
//                    return 3;
//                } else {
                return 1;
//                }
            }
        } else {
//            System.out.println(whiteCheckBoard[kingPosition[0][0]][kingPosition[0][1]]);
//            System.out.println(blackCheckBoard[kingPosition[1][0]][kingPosition[1][1]]);
            if (!check) {
                if (blackCheckBoard[kingPosition[1][0]][kingPosition[1][1]] > 0) {
                    return 2;
                }
            }
            if (whiteCheckBoard[kingPosition[0][0]][kingPosition[0][1]] > 0) {
//                System.out.println("branco ameacado");
//                if (verifyCheckMate(turn)) {
//                    return 3;
//                } else {
                return 1;
//                }
            }
        }
        return 0;
    }

    private boolean verifyCheckMate(Piece.Color turn) {
        int[][] moves = new int[8][8];
        int[] position = new int[2];
        int[] newPosition = new int[2];
        Piece.Color nextTurn = turn.equals(Piece.Color.WHITE) ? Piece.Color.BLACK : Piece.Color.WHITE;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                if (board[i][j] != null) {
                    if (board[i][j].getColor().equals(turn)) {
                        Piece piece = board[i][j];
                        position[0] = i;
                        position[1] = j;
                        moves = piece.getAvaiableMoves(position, board);
                        for (int x = 0; x < 8; x++) {
                            for (int y = 0; y < 8; y++) {
                                if (moves[x][y] == 1) {
                                    Piece enemyPiece = board[x][y];
                                    newPosition[0] = x;
                                    newPosition[1] = y;
                                    setPiece(null, position);
                                    setPiece(piece, newPosition);
                                    if (verifyCheck(nextTurn, true) != 1) {
                                        setPiece(piece, position);
                                        setPiece(enemyPiece, newPosition);
                                        return false;
                                    }
                                    setPiece(piece, position);
                                    setPiece(enemyPiece, newPosition);
                                }
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    public void setPiece(Piece piece, int[] position) {
        board[position[0]][position[1]] = piece;
    }

//    /**
//     *
//     * @param position
//     * @return [line, column]
//     */
//    public int[] getNumericPosition(String position)
//    {
//        int[] numPosition = new int[2];
//        numPosition[1] = converter.get((new Character(position.charAt(0))).toString());
//        numPosition[0] = -(position.charAt(1) - 8);
//
//        return numPosition;
//    }
    /**
     * @return the board
     */
    public Piece[][] getBoard() {
        return board;
    }

    /**
     * @param board the board to set
     */
    public void setBoard(Piece[][] board) {
        this.board = board;
    }

    /**
     * @return the unpassant
     */
    public static UnPassant getUnpassant() {
        return unpassant;
    }

    /**
     * @param aUnpassant the unpassant to set
     */
    public static void setUnpassant(UnPassant aUnpassant) {
        unpassant = aUnpassant;
    }

    /**
     * @return the turn
     */
    public Piece.Color getTurn() {
        return turn;
    }

    /**
     * @param turn the turn to set
     */
    public void setTurn(Piece.Color turn) {
        this.turn = turn;
    }
}
